package java_thinking.unit_21;

/**
 * @Desc:   并发基础理论
 *  这里对并发的一些基础问题导向：https://www.jianshu.com/p/f3d62bc16469
 *  1.如果程序中某个人物因为该程序控制范围之外的某些条件(通常是I/O)而导致不能持续运行，那么我们说这个任务
 *    或者线程阻塞了;
 *  2.编写多线程程序最基本的困难在于协调不同线程驱动的任务之间对这些资源的使用，以是的这些资源不会同时被多个
 *    任务访问
 *  3.理解：多线程、多进程、并发、并行  必看：https://blog.csdn.net/qq_39657909/article/details/80365677
 *    3.1 抽象理解
 *      线程：如果把线程比喻为一条线，那么每句代码就好像一个点，线程的工作就是从开始的点走到结束的点。
 *      多线程：就好像是有多条线，多条线"同时走"(宏观上)，有的先开始，有的后开始，有的先结束，有的后结束。
 *      进程：可以看做是由一条线或者更多条线组成的平面图形，所有的线程都有一个自己的进程，也可能多个线程拥有同一个进程。
 *      多进程：由多个平面图形组成的立体图形
 *    3.2 事例说明：
 *      多线程：迅雷下载的任务能够多个一起下载，而不是一个下载完成后才开始下载第二个，或者说在浏览器中能一边听音乐一边浏览网页
 *      多进程：同时执行多个程序，如同时运行Word编辑器,QQ等程序
 *    3.3 并发，并行：
 *      并行：当有多个CPU或者是多核CPU时才有可能实现并行，并行就是多个线程或者多个进程同时运行
 *      并发：单个CPU(也可以多个CPU)将多个线程中的每个线程(多个进程中的每个进程)按时间分为一个一个的时间片，每一个时刻
 *      只执行某个线程(进程)的时间片，时间片过期后转而执行下一个线程(进程)的时间片
 *  4.协作多线程：
 *    4.1 java的线程机智是抢占式的，这表示调度机制会周期性的中断线程，将上下文切换到另一个线程，从而为每个线程都
 *    提供时间片，使得每个线程都会分配到数量合理的时间去驱动它的任务;
 *    4.2 在协作式系统中，每个任务都会自动的放弃控制，这要求程序员要有意识的在每个任务中插入某种类型的让步语句;
 *    4.3 协作式系统的优势是双重的：上下文切换的开销通常比抢占式的系统要低廉许多，并且对可以同时执行的线程数量
 *    理论上是没有限制的
 *  5.线程与资源：
 *    线程会共享进程范围内的资源，例如内存句柄和文件句柄，但每个线程都有各自的程序计数器、栈以及局部变量等;
 *
 *  6.当线程共享数据时，必须使用同步机制，而这些机制往往会抑制某些编译器优化，使内存缓存区中的数据无效，以及增加
 *    共享内存总线的同步流量
 *
 *  7.Timer类的作用是使任务在稍后的时刻运行，或者运行一次，或者周期运行，需要确保TimeTask访问对象本身是线程安全的，
 *    从而就能把线程安全性封装共享对象内部;
 *
 *  8.非正式的说法：对象的状态是指存储在状态变量(例如实例或者静态域)中的数据
 *
 *  9.线程安全的定义：当多个线程访问某个类时，无论运行时环境采用何种调度方式，或者这些线程将如何交替执行，并且在主调代码
 *    中不需要任何额外的同步或者协同，这个类始终能表现出正确的行为，那么就称这个类是线程安全的
 *
 *  10.竟态条件：如果两个或多个线程访问相同的对象，或者访问不同步的共享状态，就会出现竟态条件。 @UnsafeCountingFactory.java
 *
 *  11.原子操作：对于访问同一个状态的所有操作(包括操作本身)来说，这个操作是一个以原子方式执行的操作
 *
 *  12.复合操作：包含了一组必须以原子方式执行的操作以确保线程安全， 如 读取-修改-写入
 *
 *  13.内置锁：即同步代码块，包括两部分 SynchronizedFactorize.java
 *    13.1 一个作为锁的对象引用
 *    13.2 一个作为由这个锁保护的代码块
 *
 *  14.重入：内置锁是可重入的，因此如果某个线程试图获得一个已经由它自己持有的锁，那么这个请求就会成功，
 *    “重入”意味着获取锁的操作粒度是“线程”，而不是“调用”;重入实现方法是锁被持有，jvm将计数加1，反则否
 *    Widget.java
 *
 *  15.synchronized锁住的是代码还是对象：synchronized锁住的是括号里的对象，而不是代码。对于非static的synchronized方法，
 *    锁的就是对象本身也就是this。区别synchronized(object)\synchronized(this) TestBlockInteger.java
 *    Java中的synchronize，通过使用内置锁，来实现对变量的同步操作，进而实现了对变量操作的原子性和其他线程对变量的可见性，从而
 *    确保了并发情况下的线程安全。https://www.jianshu.com/p/bb155747b25b
 *
 *  16.重排序：代码书写的顺序与实际执行的顺序不同，指令重排序是编译器或处理器为了提高程序性能而做的优化
 *     所以，在缺乏足够同步的多线程程序中，想对内存操作的执行顺序进行判断，几乎无法得出正确的结论 (单线程不影响)
 *     16.1 编译器优化的重排序(编译器优化)
 *     16.2 指令级并行重排序(处理器优化)
 *     16.3 内存系统的重排序(处理器优化)

 *
 *  17.内存可见性：两个线程之间共享数据需要同步可见，否则可能当前线程使用的数据是失效的，即一个线程对数据修改，另一个线程能够
 *     及时看到，共享变量更新后的值需要在工作内存与主内存间及时更新
 *     使用volatile关键字能够保证内存可见性，但不能保证原子性
 *     详见：https://www.cnblogs.com/rocomp/p/4780532.html
 *     https://blog.csdn.net/qq_35508033/article/details/68942728
 *
 *  18.JAVA内存模型要求，变量的读写操作必须是原子操作，但对于非volatile类型的long和double (64位) 的读写操作允许分解为两个
 *    32 位的操作，当对非 volatile 类型的 long和double 读写操作不在同一个线程中执行，可能会读到某个值的高32bit 和另一个值的
 *    低 32bit
 *
 *  19.volatile 正确使用方式：确保它们自身状态的可见性，确保它们所引用的对象的状态的可见性，以及表示一些重要的程序
 *     生命周期时间的发生(例如初始化或关闭)
 *
 *     仅当满足一下所有条件时，才应该使用volatile变量：
 *     19.1 对变量的写入操作不依赖变量的当前值，或者你能确保只有单个线程更新变量的值
 *     19.2 该变量不会与其他状态变量一起纳入不变性条件中
 *     19.3 在访问变量时不需要加锁;
 *
 *  20.发布(Publish)：使对象能够在当前作用域之外的代码中使用;
 *     逸出(Escape):当某个不应该发布的对象被发布时，这种情况就被称为逸出
 *
 *     封装的最主要目的：封装能够使得对程序的正确性进行分析变得可能，并使得无意中破坏设计约束条件变得更难;
 *     安全对象的构造：@PublishAndEscape.java
 *
 *  21.线程封闭：如果在单线程内访问数据，就不需要同步，它是实现线程安全性的最简单方式之一
 *     线程封闭技术的一种常见应用是JDBC，它并不要求Connection对象必须是线程安全的
 *
 *  22.Ad-hoc 线程：维护线程封闭性的职责完全由线程实现来承担;由于Ad-hoc 线程封闭技术的脆弱性
 *     因此尽量少用，应该使用更强的封闭技术如栈封闭或者 ThreadLocal 类
 *
 *  23.栈封闭：它是线程封闭的一种特例，在栈封闭中，只能通过局部变量才能访问对象，局部变量的固有属性之一
 *     就是封闭在执行线程中;不要将栈封闭与 ThreadLocal 混淆
 *
 *  24.ThreadLocal 类:这个类能使线程中的某个值与保存值的对象关联起来，它提供了get与set等菪问接口或方法，
 *     这些方法为每个使用该变量的线程都存有一份独立的副本，因此get总是返回有当前执行线程在调用set时设置的
 *     最新值;
 *     24.1 什么时候使用 ThreadLocal：当某个频繁执行的操作需要一个临时对象，例如一个缓冲区，而同时又希望避免在
 *          每次执行时都重新分配该临时对象，就可以使用，如JDBC，通过将JDBC的连接保存到ThreadLocal对象中，每个
 *          线程都会拥有属于自己的连接 如 ConnectionDispenser.java
 *
 *     24.2 哪些地方用到了 ThreadLocal：在EJB调用期间，J2EE容器需要将一个事务上下文与某个执行中的线程关联起来，
 *          通过将事务上下文保存在静态的 ThreadLocal 对象中，可以很容易实现这个功能，当框架代码需要判断当前运
 *          行的是哪个事务时，只需从这个 ThreadLocal 对象中读取事务上下文;
 *
 *     24.3 只有当线程本地值的生命周期受限于任务的生命周期时，在线程池中使用 ThreadLocal 才有意义，而在线程池的线程
 *          中不应该使用 ThreadLocal 在任务之前传递值
 *
 *  25.不可变对象：如果某个对象在被创建后其状态就不能被修改，那么这个对象就成为不可变对象，不可变对象一定是线程安全的
 *     25.1 final 并不保证对象是不变的，因为在final类型的域中可以保存对可变对象的引用,未定义final的域不一定不是不可变
 *          如 String;
 *     25.2 当满足一下条件时，对象才是不可变的：
 *          1.对象创建以后其状态就不能修改；
 *          2.对象的所有域都是final类型；
 *          3.对象是正确创建的(在对象的创建期间，this引用没有逸出 PublishAndEscape.java)
 *     25.3 正如"除非需要更高的可见性，否则将所有的域都声明为私有域" -是个良好的编程习惯
 *          除非需要某个域是可变的，否则应将其声明为final域         -也是个良好的编程习惯
 *     ThreeStooges.java
 *     OneValueCache.java
 *
 *  26.使用 volatile + 不可变对象 实现线程安全的变量缓存 ***
 *     VolatileCachedFactorize.java + OneValueCache.java
 *
 *  27.任何线程都可以在不需要额外同步的情况下安全的访问不可变对象，即使在发布这些对象时没用同步，包括对象内部的final域
 *     但是final指向的是可变对象，那么还是需要同步；
 *
 *  28.要安全的发布一个对象，对象的 引用 以及对象的 状态 必须同时对其他线程可见
 *     安全的发布方式有一下几种：
 *     28.1 在静态初始化函数中初始化一个对象的引用；
 *          * 静态初始化器由JVM在类的初始化阶段执行，由于在JVM内部存在着同步机制，因此通过这种方式初始化的对象都可以被安
 *            全的发布
 *              public static Holder holder = new Holder(2);
 *     28.2 将对象的引用保存到 volatile 类型的域，或者 AtomicReference 对象中；
 *     28.3 将对象的引用保存到正确构造对象的 final 类型域中；
 *     28.4 将对象的引用保存到一个由锁保护的域中；
 *
 *  29.线程安全库中的容器类提供了以下安全发布保证：
 *     29.1 将一个键或者值放入Hashtable、synchronizedMap、ConcurrentMap中，可以安全的将它发布给任何从这些容器中访问它的线程
 *     29.2 将元素放入Vector\CopyOnWriteArrayList\CopyOnWriteArraySet\synchronizedList\synchronizedSet中
 *     29.3 将元素放入BlockingQueue 或者ConcurrentListKedQueue中
 *     实现原理：这些工厂方法通过"装饰器"模式将容器类封装在一个同步的包装器对象中，而包装器能将接口中的方法都实现为同步方法，
 *         并将调用请求转发到底层的容器对象上，，只要包装器对象拥有对底层容器对象的唯一引用(把底层容器对象封闭在包装器中)，
 *         那么它就是线程安全的
 *
 *  30.事实不可变对象:如果对象从技术上来看是可变的，但其状态在发布后不会再改变，那么把这种对象称为 事实不可变对象(Effectively Immutable Object)
 *     该对象只要保证安全发布即可！
 *
 *  31.对象发布需求取决于它的可变性：
 *     31.1 不可变对象可以通过任意机制来发布；
 *     31.2 事实不可变对象必须通过安全方式来发布
 *     31.3 可变对象必须通过安全发布，并且必须是线程安全的或者由某个锁保护起来
 *
 *  32.在并发程序中使用和共享对象时，可使用的策略如下：
 *     32.1 线程封闭：线程封闭的对象只能由一个线程拥有，对象被封闭在该线程中，并且只能由这个线程修改；
 *     32.2 只读共享：在没有额外同步的情况下，共享的只读对象可以由多个线程并发访问，但任何线程都不能修改它，只读对象包括不可
 *          变和事实不可变对象;
 *     32.3 线程安全共享：线程安全的对象在其内部实现同步，因此多个线程可以通过对象的公有接口来进行访问，而不需要进一步同步，如29；
 *     32.4 保护对象：被保护的对象只能通过持有特定的锁访问，保护对象包括封装在其他线程安全对象中的对象，以及已发布的并且某个
 *          特定锁保护的对象;
 *
 *  --—_4 对象组合
 *
 *  33.设计线程安全类的过程包括以下三要素：
 *     33.1 找出构成对象状态的所有变量；
 *     33.2 找出约束状态变量的不变性条件；
 *     33.3 建立对象状态的并发访问管理策略 34-39,45；
 *
 *  34.状态所有权-所有权分离：容器类拥有其自身的状态，而客户代码则拥有容器中各个对象的状态，见所有权分离示意图 并发.xmind
 *
 *  35.线程封闭案例：PersonSet.java
 *
 *  36.基于监视器模式的线程安全的 车辆追踪案例 MonitorVehicleTracker.java !!!
 *
 *  37.线程安全性的委托：CountingFactorize.java
 *     CountingFactorize将他的线程安全性委托给 AtomicLong 来保证：之所以 CountingFactorize 是线程安全的，是因为 AtomicLong
 *     是线程安全的；
 *
 *  38.基于单个状态变量安全委托的车辆追踪器 DelegatingVehicleTracker.java 将车辆坐标视图委托给线程安全的容器 ConcurrentHashMap
 *
 *  39.基于多个状态变量的安全委托 VisualComponent.java
 *
 *  40.如果某个类含有复合操作，那么仅靠委托并不足以实现线程安全性，此时类必须提供自己的加锁机制来保证这些复合操作都是原子
 *     操作，除非整个复合操作都可以委托给状态变量；
 *
 *  41.如果一个状态变量是线程安全的，并且没有任何不变性条件来约束它的值，在变量的操作上也不存在任何不允许的状态转换，
 *     那么就可以安全地发布这个变量，例如 VisualComponent.java 中的变量就是安全的，因为没有在状态上对两个变量添加任何
 *     约束条件，空，或者其他，只要这两个变量是线程安全的即可；
 *
 *  42.构造一个发布其底层可变状态的车辆追踪器    PublishingVehicleTracker.java
 *
 *  43.非线程安全的案例！！ListHelper.java 非常重要！正确的客户端加锁方式 SafeListHelper.java
 *     通过这两个例子：添加一个原子操作来扩展类是脆弱的，因为它将类的加锁代码分布到多个类中，然而客户端加锁更加脆弱，
 *     因为它将类A的加锁代码放到与A完全无关的其他类中，这难以保证与约束客户端线程安全性；
 *
 *     方法锁 对象锁 类锁的区别！！！
 *
 *  44.客户端加锁机制与扩展类加锁机制共同点：都是将派生类的行为与基类的实现耦合在一起；
 *     而扩展机制会破坏实现的封装性，客户端加锁机制同样会破坏同步策略的封装性；
 *
 *  45.组合-为现有线程安全的类添加原子操作 示例 SafeListHelper.java
 *
 *  ----_5 基础构建模块
 *
 *  46.隐藏的迭代器可能线程是安全的，但是在迭代过程中可能会抛 ConcurrentModificationException
 *     常见的隐藏迭代如：日志打印线程安全对象、容器的hashCode和equals、containsAll、removeAll、retainAll
 *     以及把容器作为参数的构造函数，都会对容器进行迭代；
 *
 *  47.ConcurrentLinkedQueue:先进先出队列
 *     BlockingQueue:可阻塞的插入和获取操作，如果队列空，则获取元素将一直阻塞，直到队列出现一个可用的元素，如果队列已满
 *         则插入元素操作将一直阻塞，直到有可用空间，在“生产者-消费者”设计模式中，队塞列是非常有用的；
 *     ConcurrentSkipListMap
 *     ConcurrentSkipListSet
 *     ConcurrentHashMap：分段锁机制提高并发量
 *     CopyOnWriteArrayList:迭代期间不需要对容器进行加锁或者复制,迭代器保留一个指向底层基础数组的引用，在进行同步时只需
 *          确保数组内容的可见性，因此多线程同时迭代不会互相干扰，但每次修改都会复制底层数组，故适合迭代多于修改操作，在
 *          事件通知系统：分发通知时需要迭代已注册监听链表，并调用每个监听器，在大多数情况下，注册和注销监听器操作较少
 *     CopyOnWriteArraySet:迭代期间不需要对容器进行加锁或者复制
 *     LinkedBlockingQueue:FIFO
 *     ArrayBlockingQueue:FIFO
 *     PriorityBlockingQueue:按优先级排序，可以按自然顺序，或实现Comparator接口
 *     SynchronousQueue:没有存储功能，维护着一组线程，这些线程在等待把任务加入或移出队列，也就是可以直接交付工作，降低
 *                      生产者移动到消费者的延迟；
 *
 *  48.阻塞队列与生产者-消费者模式：将找出需要完成的工作与执行工作这两个过程分离开，并把工作项放入一个待完成列表以便
 *     随后处理，而不是找出后立即处理，消除了生产者和消费者之间的代码依赖性，另外将生成数据过程与使用数据过程进行解耦
 *     来以简化工作负载管理，因为这两个过程在处理数据的速率上有所不同；最常见的生产者-消费者模式是线程池与工作队列的组合
 *
 *  49.在构建高可靠的应用程序时，有界队列是一种强大的资源管理工具，它们能抑制并防止生产过多的工作项，使引用程序在负荷
 *     过载的情况下变的更加健壮；
 *
 *  50.InterruptedException:方法抛出该错时表示该方法是个阻塞方法，如果这个方法被中断，那么它将努力提前结束阻塞状态
 *     当调用一个方法抛出上面的异常时，调用者就变成了一个阻塞方法，并且必须要处理对中断的相应，这有两种应对策略：
 *       50.1 传递-传递给方法调用者，可以不捕获该异常，或者捕获再进行简单的清理工作后再抛出
 *       50.2 恢复中断-有时候不能抛出 InterruptedException 例如当代码是 Runnable 的一部分时，此时必须捕获异常，并通过
 *            调用当前线程上的 interrupt 方法恢复中断状态，这样在调用栈中更高层的代码将看到引发了一个中断；
 *            见 TaskRunnable.java
 *
 *  51.闭锁：一种同步工具类，可以延迟线程的进度直到其到达终止状态，闭锁可以用来确保某些活动直到其他活动都完成后才继续
 *     执行,但是一旦大门打开，所有线程都通过了，那么这个闭锁的状态就失效了，门的状态也就不能变了，只能是打开状态。也
 *     就是说闭锁的状态是一次性的，它确保在闭锁打开之前所有特定的活动都需要在闭锁打开之后才能完成。
 *
 *  52.二元闭锁：包括两个状态
 *
 *  53.使用闭锁 CountDownLatch 案例：TestHarness.java
 *
 *  54.FutureTask：可取消的异步计算。利用开始和取消计算的方法、查询计算是否完成的方法和获取计算结果的方法，此类提供了
 *     对 Future 的基本实现。仅在计算完成时才能获取结果；如果计算尚未完成，则阻塞 get 方法。一旦计算完成，就不能再重
 *     新开始或取消计算。FutureTask 在 Executor 框架中表示异步任务，此外还可用来表示一些时间较长的计算，这些计算可以
 *     在使用计算结果之前启动；
 *
 *  55.计数信号量：Semaphore 用来控制同时访问某个特定资源的操作数量，或者同时执行某个指定操作的数量，计数信号量还可以用来
 *     实现某种资源池，或者对容器施加边界；可以用于实现资源池，例如数据连接池
 *     release 方法将创建一个许可
 *     acquire 方法将消耗一个许可
 *
 *  56.计算信号量：一种简化形式二值信号量，即初始值为1的 Semaphore.二值信号量可以用作互斥体。并具备不可重入的加锁语义：
 *     谁拥有这个唯一的许可，谁就拥有了互斥锁；
 *
 *  57.如果将 Semaphore 的计数值初始化为池的大小，如果要从池中获取一个资源之前首先调用 acquire 方法获取一个许可，在资源
 *     返回给池子之后调用release释放许可，那么 acquire 将一直阻塞直到资源池不为空，在构造阻塞对象池时，可以使用 BlockingQueue
 *     来保存池的资源；
 *     Semaphore 为容器设置边界 BoundedHashSet.java
 *
 *  58.Barrier 栅栏：类似于闭锁，它能阻塞一组线程知道某个 事件发生，栅栏与闭锁的关键区别在于-所有线程必须同时到达栅栏位置，
 *     才能继续执行，闭锁用于等待事件并同时释放，而栅栏用于等待其他线程，到达后做下一步，主要用于实现一些协议；
 *
 *  59.CyclicBarrier:可以使一定数量的参与方反复的在栅栏位置汇集，这种算法通常讲一个问题拆分成一系列互相独立的子问题，当线程到达
 *     栅栏位置时将调用await方法，这个方法将阻塞直到所有线程都到达栅栏的位置，如果所有线程到到达了栅栏位置，那么栅栏将打开，此时
 *     所有线程将都被释放，而栅栏将被重置以便下次使用；
 *
 *  60.Exchanger(Two-Party)两方栅栏，各方在栅栏位置上交换数据，当两方执行不对称操作时，Exchanger 会非常有用，例如当一下线程向
 *     缓冲区写入数据，而另一个线程从缓冲区读取数据，这些线程可以使用 Exchanger 来会和，并将满的缓冲区与空的缓冲区交换，当两个
 *     线程交换时，会把这两个对象安全的发布给另一方；
 *
 *  61.构建高效且可伸缩的结果缓存 Memoizer.java !!!
 *
 *  62.总结：
 *     62.1 将委托称为实现线程安全的一个最有效策略：
 *          委托其实就是把原来通过使用synchronize和volatile来实现的线程安全，委托给Java提供的一些基础构建模块(当然你也可以写
 *          自己的基础构建模块)去实现，这些基础构建模块包括：
 *          同步容器：比如Vector，同步容器的原理大多就是synchronize，所以用的不多；
 *          并发容器：比如本文用到的ConcurrentHashMap，当然还有CopyonWriteArrayList、LinkedBlockingQueue等，根据你的需求使
 *                    用不同数据结构的并发容器；
 *          同步工具类：比如本文用到的Future和FutureTask，还有闭锁(Latches)、信号量(Semaphores)、栅栏(Barriers)等
 *     62.2 第一部分总结:
 *          1.可变状态是至关重要的
 *            所有的并发问题都可以归结为如何协调对并发状态的访问。可变状态越少，就越容易确保线程安全性。
 *          2.尽量将域声明为final类型，除非需要他们是可变的。
 *          3.不可变对象一定是线程安全的。
 *            不可变对象能极大地降低并发编程的复杂性。它们更为简单而且安全，可以任意共享而无需使用加锁或保护性复制等机制
 *          4.封装有助于管理和复杂性
 *            在编写线程安全的程序时，虽然可以将所有数据都保存在全局变量中，但为什么要这样做？将数据封装在对象中，更易于维持
 *            不变性的条件：将同步机制封装在对象中，更易于遵循同步策略。
 *          5、用锁来保护每一个可变变量。
 *          6.当保护同一个不变性条件中的所有变量时，要使用同一个锁。
 *          7.在执行复合操作期间，要持有锁。
 *          8.如果从多线程中访问同一个可变变量时没有同步机制，那么程序会出现问题。
 *          9.不要故作聪明地推断出不需要使用同步。
 *          10.再设计过程中考虑线程的安全，或者在文档中明确指出它不是线程安全的。
 *          11.将同步策略文档化。
 *
 *  ---_6 任务执行
 *
 *  63.Executor：它提供了一种标准的方法将任务的提交过程与执行过程解耦开来，并用 Runnable 来表示任务；还提供了对生命周期的支持
 *     基于生产者-消费者模式，以及统计信息收集、应用程序管理机制和性能监视等机制；
 *
 *  64.任务执行策略包括：
 *     64.1 在什么线程中执行任务?
 *     64.2 任务按照什么顺序执行(FIFO\LIFO\优先级)?
 *     64.3 有多少个任务并发执行?
 *     64.4 在队列中有多少个任务等待执行?
 *     64.5 如果系统由于过载而需要拒绝一个任务，那么应该选择哪一个任务？另外如何通知应用程序有任务被拒绝？
 *     64.6 在执行一个任务之前或之后，应该进行哪些动作？
 *     各种执行策略都是一种资源管理工具，最佳策略取决于可用的计算资源以及对服务质量的需求；
 *
 *  63.线程池：管理一组同构工作线程的资源池；
 *     优点：不仅可以在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销，另外一个好处就是当请求到达时，工作线程通常已经
 *     存在，因此不会由于等待线程创建而延迟任务的执行
 *
 *  64.通过 Executors 静态工厂方法来创建一个线程池：
 *     newFixedThreadPool:固定长度的线程池，即线程池的规模有上限。
 *     newCachedThreadPool：可缓存的线程池，如果线程池的当前规模超过了处理需求时，将回收空闲的线程，而当需求增加时，则可以添
 *          加新的线程，注意线程池的规模不存在任何限制。
 *     newSingleThreadExecutor:单线程的Executor，通过创建单个工作者线程来串行的执行任务，如果此线程异常结束，Executor会创建
 *          另一个线程来代替。注意此模式能确保依照任务在队列中的顺序来串行执行(例如FIFO、LIFO、优先级)。由工厂方法
 *          unconfigurableExecutorService封装的；
 *     newScheduledThreadPool:创建固定长度的线程池，而且以延迟或者定时的方式来执行任务。
 *
 *  65.CompletionService:它将 Executor 和 BlockingQueue 的功能融合在一起，你可以将Callable任务提交给它来执行，然后使用类似
 *     队列操作的 take 和 poll 等方法来获得已完成的结果，而这些结果会在完成时将被封装为 Future , ExecutorCompletionService
 *     实现了 CompletionService ，并将计算部分委托给一个 Executor ；
 *     案例：使用 CompletionService 使页面元素在下载完成后立即显示 Renderer.java
 *
 *  66.为任务设置时限案例 RenderWithTimeBudget.java
 *
 *  67.ExecutorService.invokeAll() 支持限时的接收Future任务集合并返回 案例：TimeBudget.java
 *
 *  -----_7 取消与关闭
 *
 *  68.任务取消：如果外部代码能在某个操作正常完成之前将其置入“完成”状态，那这个操作就可以为取消的
 *     取消的原因：
 *     68.1 用户请求取消；
 *     68.2 有时间限制的操作；
 *     68.3 应用程序事件：多个任务处理某个问题空间，当某个任务找到了解决方案时，其他任务都将被取消
 *     68.4 错误：如网页爬虫任务发生错误时那么任务会取消
 *     68.5 关闭
 *
 *  69.何时会发生线程永远不会结束的情况：例如，生产者不断向阻塞队列 BlockingQueue.put ，但是消费者
 *     如果不再进行pop 那么生产者将进入阻塞状态，此时线程池是无法 shutdown() 的；例 BrokenPrimeProducer.java
 *
 *  70.Thread:
 *     interrupt:中断线程；见 5-10 TaskRunnable.java
 *     isInterrupted:返回目标线程的中断状态
 *     interrupted(static)：清除当前线程的中断状态，返回它之前的值，这也是清除中断状态的唯一方法；
 *
 *  71.阻塞库方法，如 Tread.sleep 和 Object.wait 等，都会检查线程何时中断，并且在发现中断时提前返回，他们在响应中断时
 *     执行的操作包括：清除中断状态，并抛出 InterruptedException，表示阻塞操作由于中断提前结束，JVM不能保证阻塞方法检
 *     测到中断的速度，打在实际情况中其响应速度还是非常快的；
 *
 *  72.当线程在非阻塞状态下中断时，它的中断状态将被设置，然后根据将被取消的操作来检查中断状态以判断发生了中断，通过这
 *     样的方法中断操作将变得有粘性--如果不触发 InterruptedException 那么中断状态将一直保持，直到明确的清除中断状态；
 *
 *  73.调用 interrupt 并不意味着立即停止目标现成正在进行的工作，而只是传递了请求中断的消息；
 *
 *  74.中断策略规定线程如何解释某个中断请求：当发现中断请求时，应该做哪些工作，哪些工作单元对于中断来说是原子操作，
 *     以及以多块的速度来响应中断；
 *
 *  75.最合理的 中断策略 是某种形式的 线程级(Thread-level) 取消操作或者 服务级(Service-level)取消操作：尽快退出，在必
 *     要时进行清理，通知某个所有者该线程以及退出；
 *
 *  76.有的任务不会再自己拥有的线程中执行，而是在某个服务(例如线程池)拥有的线程中执行，对于非线程所有者的代码来说应该
 *     小心保存中断状态，这就是为什么大多数可阻塞的库函数都只是抛出 InterruptedException 作为中断响应；他们永远不会
 *     在某个由自己拥有的线程中执行库函数，因此他们为任务或者库代码实现了最合理的取消策略：尽快退出执行流程，并把中断
 *     信息传递给调用者，从而使调用栈中的上层代码可以采取进一步的操作；！！！
 *
 *  77.Thread.currentThread().interrupt()：如果除了将 InterruptedException 传递给调用者之外还要执行某些操作，那么
 *     应该在本线程捕获 InterruptedException 之后恢复中断状态，再将中断状态进行保存或其他需要给上层知道的操作；
 *
 *  78.由于每个线程拥有各自的中断策略，因此除非你知道中断对该线程的含义，否则就不应该中断这个线程；线程应该只能由其所有者
 *     中断(被调用者)，所有者可以将线程的中断策略信息封装到某个合适的取消机制中，例如关闭 shutdown 方法
 *  
 *  79.响应中断：只有实现了线程中断策略的代码才可以屏蔽中断请求，在常规的任务和库代码中都不应该屏蔽中断请求；
 *
 *  80.不可取消的任务在退出前恢复中断 NoncancelableTask.java
 *
 *  81.不要在外部线程中安排中断,反面案例 TimedRun1.java
 *
 *  82.通过 Future 来实现取消：Future 有个cancel 方法，该方法带有一个 boolean 类型的参数 mayInterruptIfRunning ，表示
 *     取消操作是否成功(这只是表示任务是否能够接收中断，而不是表示任务是否能检测并处理中断)，如果 mayInterruptIfRunning
 *     为true 并且任务当前正在某个线程中运行，那么这个线程能被中断，如果为 false 那么意味着"任务还没启动，不要运行它"，
 *     这种方式应该用于哪些不处理中断的任务中；如果任务在 Executor 中创建的，那么可以通过中断被取消
 *     案例 TimedRun.java
 *
 *  83.并非所有的可阻塞方法或者阻塞机制都能相应中断：如果一个线程由于执行同步的 Socket I/O 或者等待获得内置锁而阻塞，那么
 *     中断请求只能设置线程的中断状态，除此之外没有其他任何作用，对于那些由于执行不可中断操作而被阻塞的线程，可以使用类似于
 *     中断的手段来停止这些线程，但这要求发出中断请求手段的代码知道线程是为什么阻塞的；
 *     1 JAVA.io 包中的同步 Socket I/O：在服务器应用程序中，最常见的阻塞 I/O 形式就是对套接字进行读取和写入，虽然 InputStream
 *       和 OutputStream 的 read 和 write 等方法都不会响应中断，但可以通过关闭底层的套接字，可以使得执行 read 和 write
 *       等方法被阻塞的线程抛出一个 SocketException；
 *     2 JAVA.io 包中的同步 I/O ：当中断一个正在 InterruptibleChannel 上等待的线程时，将抛出 CloseByInterruptException
 *       关闭链路(这还会使得其他在这条链路上阻塞的线程同样抛出 CloseByInterruptException )，当关闭一个 InterruptibleChannel
 *       时，将导致所有在链路上阻塞的线程都抛出 AsynchronousCloseException , 大多数标准的 Channel 都实现了 InterruptibleChannel
 *     3 Selector 的异步 I/O :如果一个线程在调用 Selector.select 方法时阻塞了，那么调用close 或者 wakeup 方法会使线程抛出
 *       ClosedSelectorException 并提前返回；
 *     4 获取某个锁: 如果一个线程由于等待某个内置锁而阻塞，那么将无法响应中断，因为线程认为它肯定会获得锁，所以将不会理会
 *       中断请求，但是，在lock 类中提供了 lockInterruptibly 方法，该方法允许在等待一个锁的同时，仍能响应中断，见13章
 *
 *     综上：如何封装非标准的取消操作 ReaderThread.java，对于那些不可中断的阻塞，想办法从线程根本处关闭阻塞方法，如关闭套接字
 *     然后再处理标准的中断,可以通过改写 interrupt 方法，使用 newTaskFor 来封装 SocketUsingTask.java
 *
 *  84.停止基于线程的服务封装原则：
 *     1 除非拥有某个线程，否则不能对该线程进行操控(例如中断线程或者修改线程的优先级)
 *     2 线程有一个相对应的所有者，即创建该线程的类，因此线程池是其工作者线程的所有者，如果要中断这些线程，那么应该使用线程池
 *
 *  85.线程的所有权是不可传递的：应用程序可以拥有服务，服务也可以拥有工作者线程，但应用程序并不能拥有工作者线程，因此应用程序
 *     不能直接停止工作者线程，服务应该提供生命周期方法来关闭它自己所拥有的线程，这样，当应用程序关闭该服务时，服务就可以关闭
 *     所有线程了。
 *
 *  86.对于持有线程的服务，只要服务的存在时间大于创建线程的方法的存在时间，那么就该提供生命周期的方法；
 *
 *  87.多生产者单消费者(multiple-producer,single-consumer)生产者模式：如日志服务，每个调用log的操作都相当于一个生产者，
 *     而后台的日志线程则相当于消费者；如果消费者速度慢与生产者速度，则会阻塞生产者线程(BlockingQueue.put)
 *     反面案例 LogWriter.java
 *     较好案例
 *
 *  88.将 ExecutorService 封装在某个更高级的服务中，并且该服务能够提供使其自己的生命周期方法 如：
 *
 *  89.毒丸对象：指一个放在队列上的对象，含义是:当得到这个对象时，立即停止，在FIFO(先进先出)队列中，毒丸对象将确保消费者
 *     在关闭之前首先完成队列中的所有工作，在提交毒丸对象之前的所有工作都会被处理，而生产者在提交了毒丸对象之后，将不会再
 *     提交任何工作；只有在无界队列中，毒丸对象才能可靠的工作
 *     案例 IndexingService.java
 *
 *  90.只执行一次的多线程服务案例：CheckForMail.java
 *
 *  91.shutdownNow 只会返回已提交但未开始的任务，为了能返回正在执行的线程未正常完成的任务 案例：TrackingExecutor.java
 *     TrackingExecutor 的爬虫应用案例：WebCrawler.java 这里有 幂等 情况
 *
 *  92.处理非正常的线程终止：如何在线程池内部构建一个典型的工作者线程：TypicalThreadPool.java;
 *
 *  93.UncaughtException:能够检测出线程由于未捕获的异常而终结，与92互补，就能有效防止线程泄漏问题；
 *
 *  94.如果希望在任务发生异常而失败时获得通知，并且执行一些特定于任务的恢复操作，那么可以将任务封装在能捕获异常的 Runnable
 *     或者 Callable 中，或者改写 ThreadPoolExecutor 的 afterExecutor 方法；只有通过 execute 提交的任务，才能将它抛出的
 *     异常讲给未捕获异常处理器，而通过 submit提交的任务，无论是抛出的未检查异常还是已检查异常，都将被认为是任务返回状态的
 *     一部分，如果 submit 提交的任务抛出异常结束，那么这个异常将被Future.get 封装在 ExecutionException 中重新抛出；
 *
 *  95.钩子：关闭钩子是指通过 Runtime.addShutdownHook 注册的但尚未开始的线程；为了并发的关闭钩子，要对所有服务使用同一个
 *     关闭钩子，避免在关闭操作之间出现竟态条件或者死锁等问题；
 *
 *  96.守护线程：线程可分为两种：普通线程和守护线程。在JVM启动时创建的所有线程中，除了主线程以外，其它的线程都是守护线程
 *     (例如垃圾回收器以及其它执行辅助工作的线程)。当创建一个新线程时，新线程将继承创建它的线程的守护状态。因此在默认情
 *     况下，主线程创建的所有线程都是普通线程。
 *     普通线程与守护线程之间的差异仅在于当线程退出时发生的操作。当一个线程退出时，JVM会检查其他正在运行的线程，如果这些
 *     线程都是守护线程，那么JVM会正常退出操作。当JVM停止时，所有仍然存在的守护线程都将被抛弃——既不会执行finally代码块，
 *     也不会执行回卷栈，而JVM只是直接退出。
 *     示例：https://www.jianshu.com/p/7bdc874e3ea4
 *
 *
 *  ---8 线程池的使用
 *
 *  97.只有当任务都是同类型的，并且相互独立时，线程池的性能才能达到最佳；
 *
 *  98.线程饥饿死锁：所有正在执行任务的线程都由于等待其他仍处于工作队列的任务而阻塞；
 *
 *  99.要想正确的设值线程池的大小，必须分析计算环境、资源预算和任务特性，在部署的系统中有多少个CPU，多大的内存，任务是计算
 *     密集型、I/O密集型还是二者皆可，是否需要像JDBC这样的稀缺资源；也可以根据Runtime.availableProcessors来动态计算
 *
 *  100.对于计算密集型任务，线程池大小为Ncpu + 1 时通常能实现最优的利用率，这个额外的线程能确保CPU的时钟周期不会被浪费；
 *      对于 I/O 操作性任务，需要估算出任务的等待时间与计算时间比值，另一种方法是设置不同线程池，观察CPU
 *
 *  101.线程池大小计算：
 *          Ncpu = number of CPUs
 *          Ucpu = target CPU utilization, 0 ≤ Ucpu ≤ 1
 *          W/C = ratio of wait time to compute time
 *      要使处理器达到期望 的使用率，线程池大小 最优等于：
 *          N(threads) = Ncpu * Ucpu * (1+W/C);
 *      可以通过Runtime 来获得 CPU 数目：
 *          int N_CPUS = Runtime.getRuntime().availableProcessors();
 *      但CPU周期并不是唯一影响线程池大小的资源，还包括内存、文件句柄、套接字句柄和数据库连接等，计算这些资源约束条件：
 *      计算每个任务对该资源的需求量，然后用该资源的可用总量除以每个任务的需求量，所得结果就是线程池的大小上限；
 *
 *  102.可以通过配置 ThreadPoolExecutor 来定制线程池
 *      newFixedThreadPool:固定长度的线程池，即线程池的规模有上限。而且创建的线程池不会超时；
 *      newCachedThreadPool：将线程池的最大大小设置为Integer.MAX_VALUE，而将基本大小设置为零，并将超时设置为1min，
 *                          这样创建出来的线程可以被无限扩展，并且需求降低时会自动收缩；
 *
 *  103.ThreadPoolExecutor 允许提供一个 BlockingQueue 来保存等待执行的任务，
 *      基本的任务排队方法有3种：无界队列、有界队列、同步移交,稳妥的是用有界的队列
 *          newFixedThreadPool:默认使用无界的 LinkedBlockingQueue
 *          newSingleThreadExecutor:默认使用无界的 LinkedBlockingQueue
 *          newCachedThreadPool：使用 SynchronousQueue 在线程之间进行移交任务，在放入元素前必须要有另一个线程正在
 *                              这等待接受这个元素，如果无则创建线程知道达到最大线程值，否则根据饱和策略这个任务被
 *                              拒绝；
 *      beforeExecute：如果在 beforeExecute 抛出一个 RuntimeException 那么任务将不被执行，afterExecute 也不会调用
 *      afterExecute：无论是从run正常返回，还是抛出一个异常而返回，都会被调用
 *      terminated：线程池完成关闭操作时调用，可以用来释放Executor 在其生命周期里分配的各种资源，还可以发送通知，日志
 *                  收集 finalize 统计信息等操作；
 *  104.饱和策略：
 *      1 中止(Abort):
 *      2 调用者运行(Caller-Runs):
 *
 *  105.使用 Semaphore (信号量) 来限制任务的到达率 见 BoundedExecutor.java
 *
 *  106.代码安全库访问权限的安全策略 P162
 *
 *  107.扩展线程池 ThreadPoolExecutor TimingThreadPool.java
 *
 *
 *  108.分解数据模型：如果一个数据模型必须被多个线程共享，而且由于阻塞、一致性或复杂度等原因而无法实现一个线程安全的模型
 *      时，可以考虑使用分解模型设计；需要扩展
 *
 *  ----——10 避免活跃性危险
 *
 *  109.死锁：每个线程拥有其他线程需要的资源，同时又等待其他线程已经拥有的资源，并且每个线程在获得所有需要的资源之前都不会
 *      放弃已经拥有的资源---抱锁；
 *  110.锁顺序死锁：两个线程试图以不同的顺序来获得相同的锁，如果按照相同的顺序来请求锁，那么就不会出现循环的加锁依赖性，也
 *      不会产生死锁；
 *  111.指定锁的顺序时，可以使用 System.identityHashCode 方法，该方法将返回由 Object.hashCode 返回值，来避免锁顺序死锁
 *      案例 InduceLockOrder.java ！！！
 *
 *  112.在协作对象之间发生的死锁 CooperatingDeadlock.java 开放调用来避免协作对象之间发生死锁 CooperatingNoDeadlock
 *
 *  113.通过线程转储信息来分析死锁：
 *      1.线程转储包括各个运行中的线程的栈追踪信息，类似于发生异常时的栈追踪信息，
 *      2.线程转储还包含加锁信息，例如每个线程持有了哪些锁，在哪些栈帧中获得这些锁，以及被阻塞的线程正在等待获取哪个锁；
 *      JVM将在等待关系图中通过搜索循环来找出死锁，若发现死锁，则获取响应的死锁信息，例如在死锁中涉及哪些锁和线程，以及
 *      这个锁的获取操作位于线程的哪些位置；
 *
 *  114.如何触发线程转储操作：
 *      1.UNIX:可以向JVM的进程发送 SIGQUIT 信号(kill -3),或者在UNIX平台中按下 Ctrl-\键
 *      2.在Windows平台中按下 Ctrl-Break 键；
 *
 *  115.显式 Lock 与内置锁存在的转储功能差异：
 *      1.java 5.0不支持与 Lock 相关的转储信息，在线程转储中不会出现显式的 Lock
 *      2.java 6.0包含对线程转储和死锁检测等的支持，但在这些锁上获得的信息比在内置锁上获得的信息精确度低  ？？
 *      3.内置锁与获得他们所在的线程栈帧是相关联的，而显式的 Lock 只与获得它的线程相关联；  ？？
 *  116.线程优先级：尽量不要改变线程的优先级，因为这会增加平台的依赖性，并可能导致活跃性问题，大多数并发应用程序中都可以使用
 *      默认的线程优先级；
 *
 *  117.活锁：另一种形式的活跃性问题，尽管活锁不会阻塞线程，但也无法继续执行线程应该执行的任务，因为线程将不断的重复执行
 *      相同的操作，而且总会失败
 *      案例：处理事务消息的应用程序中：如果不能成功的处理某个消息，那么消息处理机制将回滚整个事务，并将重新放到队列的头；
 *
 *  118.何时发生活锁：当多个相互协作的线程都对彼此进行响应从而修改各自的状态，并使得任何一个线程都无法继续执行时，就会发生
 *
 *  119.如何避免活锁：在并发应用程序中，通过等待随机长度时间和回退可以有效的避免活锁的发生,在重试机制中引入随机性；
 *
 *  -------_11 性能与可伸缩性 https://www.cnblogs.com/HectorHou/p/6040872.html
 *
 *  120.应用程序的性能可以采用多个指标来衡量，例如服务时间、延迟时间、吞吐率、效率、可伸缩性以及容量等；
 *      运行速度：服务时间、等待时间；
 *      处理能力：生产量、吞吐量，即在计算资源一定的情况下，能完成多少工作
 *
 *  121.可伸缩性：当增加计算资源时(例如 CPU、内存、存储容量、I/O 带宽)，程序的吞吐量或者处理能力能相应的增加；
 *
 *  122.性能调优：目的是更小的代价完成相同的工作，例如通过缓存来重用之前的计算结果，或者采用时间复杂度O(n²) 算法来代替
 *              复杂度为O(n log n)的算法
 *      可伸缩性调优：目的是设法将问题的计算并行化，从而能利用更多的计算资源来完成更多的工作；
 *
 *  123.性能的 “多快”-“多少” 是完全独立的，甚至有时候是相互矛盾的，需要注意的是有时候提高单线程性能的技术，往往会破坏可伸缩性
 *      例如：三层程序模型，模型中的表现层、业务逻辑层和持久化层彼此独立，并且可能由不同的系统来处理，如果把三层都融合到单个
 *      应用程序中，在处理第一个任务时，其性能要高于三个层之间系统的性能，但是避免不了三层之间的网络延迟等，但是想要进一步提高
 *      单线程的程序性能，往往会破坏可伸缩性；
 *
 *  124.为了使程序更快之前需要先确认几个问题：
 *      1 更快的含义是什么？
 *      2 该方法在什么条件下会运行的更快？在低负载还是高负载的情况下？大数据集还是小数据集？能都通过测试结果来验证答案？
 *      3 这些条件在运行环境中的发生频率？能否通过测试结果来验证你的答案？
 *      4 在其他不同条件的环境中能否使用这里的代码？(是否依赖计算机环境)
 *      5 在实现这种性能提升时需要付出哪些隐含的代价，例如增加开发风险或维护开销？这种权衡是否合适？
 *
 *  125.Amdahl 定律：在增加计算资源的情况下，程序理论上能够实现最高加速比，这个值取决于程序中可并行组件与串行组件所占的比重
 *      公式见图 Amdahl定律加速公式.png ，公式中N趋近无穷大时，最大的加速比趋近于 1/F，如果程序有 50% 的计算需要执行串行
 *      那么最高的加速比最大只能趋近于2，如果程序中有 10% 的计算需要串行执行，那么最高的加速比将接近 10；
 *      另外，Amdahl 定律还量化了串行化的效率开销，在拥有10个处理器的系统中，如果程序有 10% 的部分需要串行执行，那么最高的
 *      加速比为5.3(53%的使用率)，在100个处理器的系统中，加速比可以达到9.2，即使拥有无限多的CPU，加速比也不可能为10；
 *
 *  126.降低锁粒度的技术：锁分解(将一个锁分解为两个锁)和锁分段(把一个锁分解为多个锁)，通过Amdahl 定律分析时发现，锁分解似乎
 *      并不能充分利用多处理器的能力，而索芬端技术似乎更有前途，因为分段的数量可随着处理器数量的增加而增加；
 *
 *  127.上下文切换：如果可运行的线程数大于 CPU 数量，那么操作系统最终会将某个正在运行的线程调度出来，从而使其他线程能够使用
 *      CPU ，这将导致上下文切换，在这个过程中保存当前运行程序的执行上下文，并将新调度进来的线程的执行上下文设置为当前上下文；
 *      上下文切换将导致一些缓存缺失，因而线程在首次调度运行时会更加缓慢；
 *      UNIX vmstat、mpstat 命令和 Windows 系统的 perfmon 工具都能报告上下文切换次数以及在内核中执行时间所占比例等信息；
 *
 *  128.内存同步：内存栅栏：在synchronized和volatile提供的可见性保证中可能会使用一些特殊指令
 *      内存栅栏可以刷新缓存，在内存栅栏中，大多数操作都是不能被重排序的；
 *      解决：
 *      JVM优化去掉不会发生竞争的锁
 *      找出不需同步的本地栈元素
 *      锁粒度粗化，将近邻的锁合并，减少锁请求和锁释放的次数
 *
 *  128.JVM 在实现阻塞行为时，可以采用自旋等待(Spin-Waiting，指通过循环不断的尝试获取锁)，直到成功或者通过操作系统挂起被阻塞
 *      的线程，这两种方式的效率高低，要取决于上下文切换的开销，以及成功获取锁之前需要等待的时间，如果等待时间较短，则适合采用
 *      自旋等待方式，而如果等待时间较长，则适合采用线程挂起方式，有些JVM会根据对历史等待时间的分析数据在这两者之间进行选择，
 *      但是大多数 JVM 在等待锁时只是线程挂起；
 *
 *  129.当线程无法获得某个锁或者在某个条件等待或在 I/O 操作上阻塞时，需要被挂起，在这个过程中将包含两次额外的上下文切换，以及
 *      所有必要的操作系统操作和缓存操作；
 *
 *  130.串行操作会降低可伸缩性，并且上下文切换也会降低性能，在锁上发生竞争将同时导致这两种问题，因此减少锁的竞争能够提高性能
 *      和可伸缩性；
 *
 *  131.在并发程序中，对可伸缩性的最主要威胁就是独占方式的资源锁；有两个因素将影响在锁上发生竞争的可能性：锁的请求频率，以及
 *      每次持有锁的时间；
 *      1.Little 定律：在一个稳定的系统中，顾客的平均数量等于他们的平均到达率乘以在系统中的平均停留时间，这也是排队理论的一个
 *        推论；
 *
 *  132.三种降低锁的竞争程度：
 *      1 减少锁的持有时间
 *      2 降低锁的请求频率
 *      3 使用带有协调机制的独占锁，这些机制允许更高的并发性
 *
 *  133.缩小锁的范围（“快进快出”）
 * 　　 目标：尽可能缩短持有锁的时间
 * 　　 方法：超出共享变量，只对操作共享变量的代码加锁
 * 　　 理论：根据 Amdahl 定律，减少了必须串行执行的部分
 * 　　 注意：必要的原子操作不能分别加锁；锁粒度细化造成更多的同步开销，JVM会自动进行锁粒度粗化
 *
 *  134.锁分解：如果一个锁需要保护多个相互独立的状态变量，那么可以将这个锁分解为多个锁，并且每个锁只保护一个变量，从而提高
 *      可伸缩性，并最终降低每个锁被请求的频率。对竞争适中的锁进行分解时，实际上是把这些锁转变为”非竞争“的锁，从而有效地提
 *      高性能和可伸缩性。
 *
 *  135.锁分段：将锁分解技术进一步扩展为对 一组独立对象上的锁分解，
 *      案例：ConcurrentHashMap 使用了一个包含 16 个锁的数组，每个锁保护所有散列桶的 1/16，大约能把对锁的请求减少到原来的
 *      1/16 正式这项技术使得 ConcurrentHashMap 能够支持多达 16 个并发的写入， StripedMap.java
 *
 *  136.锁分段劣势：与采用单个锁来实现独占访问相比，要获取多个锁来实现独占访问将更加困难并且开销更高，例如当 ConcurrentHashMap
 *      需要扩展映射范围时就要获取分段锁集合中的所有锁，要获取内置锁的一个集合，唯一的方法就是递归；
 *
 *  137.锁分解和锁分段技术都能提高可伸缩性，因为他们都能使不同的贤臣在不同的数据上操作，或者同一个数据不同部分上的操作；
 *
 *  138.什么情况下使用锁分解或分段技术：程序一定要表现出在锁上的竞争频率高于在锁保护的数据上发生竞争的频率
 *      例：ConcurrentHashMap和Map中的每一項;
 *
 *  139.性能与可伸缩性之间的制约：当每个操作都请求多个变量时，锁的粒度将很难降低；
 *      常见的措施：将一些反复的计算结果进行缓存，引入一些 “热点域” (Hot Field)，而这些热点域往往会限制可伸缩性；
 *
 *  140.热点域：数据上发生很高频率的竞争（例：HashMap.size());例如在HashMap中加入计数器，计数有多少元素，但是每次写入删除
 *      都要获得计数器的锁，因此也成为了热点域为了避免这个问题 ConcurrentHashMap 对每段元素进行枚举计数，而不是维护一个全局
 *      技术
 *
 *  141.取代独占锁:1.只读不用锁 2.使用原子变量降低热点域开销(但不能完全消除)
 *
 *  142.监控CPU利用率：linux命令：vmstat或mpstat
 *      cpu利用不充分的原因：
 *          1 负载不充足。
 *          2 I/O密集。Unix可用 iostat, windows用 perfmon。
 *          3 外部限制。如数据库服务，web服务等。
 *          4 锁竞争。可通过jstack等查看栈信息。出发一些线程转储并在其中查找在锁上发生竞争的线程，如果线程等待锁被阻塞，那么
 *            线程转储信息中将存在相应的栈帧，对于竞争激烈的锁，通常至少会有一个线程在等他，因此将在线程转储中频繁出现；
 *
 *  143.对象池：尽管对象池技术能降低垃圾收集操作的开销，但对于高开销对象以外的其他对象来说，仍然存在性能缺失，因为，除了损失
 *      CPU 指令周期外，在对象池技术中存在的一个最大问题就是如何正确的设定对象池的大小，如果对象池太小那将没什么作用，如果对
 *      象池过大将占用其他程序需要的内存资源，如果在重新使用某个对象时没有将其恢复到正确的状态，那可能就会产生一些微妙的错误，
 *      还可能出现一个线程在讲对象归还给线程池后仍然使用该对象的问题，从而产生一种从旧对象到新对象的引用，这会导致基于代的垃
 *      圾回收期需要执行更多的工作；
 *
 *  144.当线程分配新的对象时，基本上不需要在线程之间进行协调，因为对象分配器通常会使用线程本地的内存块，所以不需要在堆数据结
 *      构上进行同步。然而，如果这些线程从对象池中请求一个对象，那么就需要通过某种同步来协调对象池数据结构的访问，从而使某个
 *      线程被阻塞。对象分配操作的开销比同步的开销更低。
 *
 *  145.减少上下文的开销：减少锁的持有时间，因为持有时间越长，就越容易发生竞争，就月容易发生阻塞。当任务在运行和阻塞这两个状
 *      态之间转换时，就相当于一次上下文切换。
 *      1 在服务器应用程序中，发生阻塞的原因之一就是在处理请求时产生各种日志消息，日志操作的服务时间包括与 I/O 流类相关的计
 *        算时间，如果 I/O 阻塞还会有线程阻塞时间。即日志操作包含了 I/O 操作和加锁操作，从而导致上下文切换次数的增多，以及
 *        服务时间的增加；
 *
 *  146.类似 LogWriter.java 通过把 I/O 操作从处理请求的线程转移到一个专门的线程这将提升整体的吞吐率，减少了调度资源，上下
 *      文切换次数更少，锁管理更简单；
 *
 *  147.Amdahl 定律告诉我们：程序的可伸缩性取决于所有代码中必须被串行执行的代码比例，串行操作的主要来源是独占方式的资源锁，
 *      可通过一下方式提升可伸缩性：
 *          1 减少锁的持有时间
 *          2 降低锁粒度
 *          3 采用非独占锁或非阻塞锁来代替独占锁
 *
 *  --------—_12 并发程序的测试
 *
 *  148.并发测试分为两类：
 *      1 安全性测试：不发生任何错误的行为        通常采用测试不变性条件的形式进行安全性测试即判断某个类的行为时否与其规范
 *                                              保持一致
 *      2 活跃性测试：某个良好的行为终究会发生    即性能测试衡量：
 *                                              1 吞吐量：指一组并发任务中已完成任务的占比
 *                                              2 响应性：指请求从发出到完成之间的时间(也称为延迟)
 *                                              3 可伸缩性：指在增加更多资源(常指CPU)的情况下吞吐量(或者缓解短缺)的提升
 *
 *      注：测试代码本身会带来一些影响：在增加调试代码或测试代码后消失的错误称为“海森堡错误”-来源于海森堡测不准原理；
 *
 *  149.正确性测试-基本单元测试
 *      缓存对象 SemaphoreBoundedBuffer.java
 *      测试对象 SemaphoreBoundedBufferTest.java
 *
 *  150.阻塞操作的测试：
 *      需要引入额外的复杂性：当方法被成功的阻塞后，还必须是方法解除阻塞，实现这个功能的一种简单方式就是使用中断-在一个单独
 *      的线程中启动一个阻塞操作，等到线程阻塞后再中断它，然后宣告阻塞操作成功，当然这要求阻塞方法通过提前返回或抛出
 *      InterruptedException 来响应中断 SemaphoreBoundedBufferTest.java
 *
 *  151.并发安全性测试 生产者-消费者 PutTakeTest.java
 *
 *  152.资源管理测试：测试的另一个方面就是要判断类中是否没有做它不应该做的事情，例如资源泄漏。
 *      Test_21_12.SemaphoreBoundedBufferTest#testLeak()
 *
 *  153.回调函数的执行通常时在对象生命周期的一些已知位置上，并且在这些位置上非常适合判断不变性条件是否被破坏。
 *
 *  154.使用 Thread.yield 产生更多的交替操作
 *
 *  155.性能测试案例 TimedPutTakeTest.java !!
 *
 *  156.比较不同的阻塞队列的实现：发现 LinkedBlockingQueue 的可伸缩性要高于 ArrayBlockingQueue ，初看起来这有些奇怪，会觉得
 *      链表队列在每次插入元素时，都必须分配一个链表节点对象，这似乎比基于数组队列执行了更多的工作。然而，虽然它拥有更好的内
 *      存分配与GC等开销(ArrayBlockingQueue),但与基于数组的队列(ArrayBlockingQueue)相比，链表队列的put和take等方法支持更高
 *      的并发访问，因为一些优化的链接队列算法能将队列的头结点的更新操作与尾节点的更新操作分离开来；
 *          由于内存分配操作通常是线程本地的，因此如果算法能通过多执行一些内存分配操作来降低竞争程度，那么这种算法通常具有更高
 *      的可伸缩性(这种情况再次证明了，基于传统性能的调优的直觉与提升可伸缩性的实际需求是背道而驰的)
 *      图见 比较不同的阻塞队列实现.png
 *
 *  157.响应性衡量：某个动作经过多长时间才能执行完成，此时就要测量服务时间的变化情况，即测量变动性；
 *      非公平信号量(隐蔽栅栏)：吞吐量相对公平模式大，但变动性大
 *      公平信号量(开放栅栏)：通常公平信号量将极大的降低吞吐量，但变动性小
 *      缓存过小时：将导致非常多的上下文切换次数，这即使在非公平模式中也会导致很低的吞吐量，因此几乎每个操作都会执行上下文切换；
 *
 *
 *      结论：除非线程由于密集的同步需求而被持续的阻塞，否则非公平的信号量通常能实现更好的吞吐量，而公平的信号量则实现更低的
 *          变动性，因为这些结果之间差异较大，所以 Semaphore 要求客户选择针对哪一个特性进行优化；
 *
 *
 *  158.性能测试时应该考虑垃圾回收带来的影响，通常有两种策略
 *      1 在测试过程确保垃圾回收都不执行
 *      2 在测试过程确保垃圾回收执行多次(更能反映实际环境下性能)
 *
 *  159.JVM 动态编译对测试带来的影响：
 *      1 当某个类第一次被加载时，JVM 会通过解译字节码的方式来执行它，如果某个方法运行的次数够多，那么动态编译器会将它编译
 *        为机器代码，编译完成后，代码的执行方式从解释执行变成直接执行；
 *      2 只有在所有代码都编译完成以后，才应该统计测试运行时间，测量采用解释执行的代码速度是没有意义的，因为大多数程序在运
 *      行足够长的时间后，所有频繁执行的代码路径都会被编译；
 *
 *  160.在 HotSpot 中，如果运行程序时使用命令行选项 --xx:+PrintCompilation,那么当动态编译运行时，将输出一条信息，你可以
 *      通过这条消息来验证动态编译是在测试运行前，而不是在运行过程中执行;
 *
 *  161.在测试程序中应尽量删除那些无用代码，因为优化编译器能找出并消除那些不会对输出结果产生任何影响的无用代码，在测试程序
 *      中每个计算结果都应该被使用，而且还应该是不可预测的，否则一个只能的动态优化编译器将用预先计算的结果来代替计算过程；
 *
 *  ---------_13 显示锁
 *
 *  162.ReentrantLock: 它并不是一种替代内置锁的方法，而是当内置加锁机制不适用时，作为一个可选的高级功能；是对Lock接口的实现
 *
 *  163.Lock: 提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作，所有加锁和解锁的方法都是显示的
 *
 *  162.ReentrantLock锁与内置锁synchronized  https://blog.csdn.net/wenhuayuzhihui/article/details/54930388
 *      内置锁存在的问题：
 *          1 无法中断正在等待获取锁的线程，或者无法在请求获取一个锁时无限的等待下去；
 *          2 无法实现非阻塞结构的加锁规则，也就是加锁时肯定会阻塞；
 *      显示锁相对问题：
 *          1 使用稍复杂,必须在finally块释放锁，否则抛异常无法释放，且很难追踪，这就是不能完全替代内置锁的原因：它更加危险；
 *            因为当程序离开被保护的代码块时，不会自动清除锁；
 *
 *      内置锁优点：
 *          1 内置锁的获取和释放等操作都是基于代码块的，所以无需考虑控制权如何退出该代码块；
 *          2 java 5.0 内置锁可以在线程转储中获得相关锁与线程的关系信息等，ReentrantLock 则无，但在 java 6.0 解决了这个问题
 *      显示锁优点：
 *          1 基于可定时与可轮询的锁获取模式(tryLock)因此与无条件锁获取模式相比具有更完善的错误恢复机制-避免死锁的发生-降低
 *            活锁的发生可能性(java_thinking/unit_21/_21_13/DeadlockAvoidance.java:29) TimedLocking.java；
 *          2 可中断锁获取操作：lockInterruptibly 方法能够在获得锁的同时保持对中断的响应 InterruptibleLocking.java
 *  163.在公平的锁中，如果有另一个线程持有这个锁或者其他线程在队列中等待这个锁，那么新发出的线程将被放入队列中
 *      在非公平的锁中，只有当锁被某个线程持有时，新发出请求的线程才会被放入队列中；
 *
 *  164.在大多数情况下，非公平锁的性能要高于公平锁的性能；案例 比较由公平锁以及非公平的 ReentrantLock 包装的 HashMap 的
 *      性能见图： 公平锁与非公平锁的性能比较.png
 *      在激烈竞争的情况下，非公平锁的性能高于公平锁的性能原因：在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的
 *      延迟，假设A持有一个锁，此时B请求该锁，但被A持有，B被挂起，当A释放锁，B被唤醒，B再次尝试获得锁，如果此时是非公平锁，
 *      有一个C也请求这个锁，那么C很可能会在B被完全唤醒之前获得、使用这个锁，即：B获得锁时刻并没有延迟，C更早获得了锁，并且
 *      吞吐量也获得了提高；总结一句：让锁被持有的时间密度更大，更高的利用率；但若持锁时间较长则效果将不明显
 *
 *  165.在一些内置锁无法满足需求的情况下，ReentrantLock 可以作为一种高级工具，当需要一些高级功能时才应该使用 ReentrantLock
 *      ,这些功能包括：可定时的、可轮询的与可中断的锁获取操作，公平队列，以及非块结构的锁，否则还是有限使用 synchronized;
 *
 *  166.读写锁：一个资源可以被多个读操作访问，或者被一个写操作访问，但两者不能同时进行，当锁的持有时间较长，并且大部分操作都
 *      不会修改被守护的资源时，那么读写锁能够提供并发性，如使用读写锁的Map ReadWriteMap.java
 *
 *  167.ReadWriteLock:
 *      可选实现：
 *          1 释放优先：写入锁释放后，应该优先选择读线程，写线程，还是最先发出请求的线程；
 *          2 读线程插队：锁由读线程持有，写线程再等待，再来一个读线程，是继续让读线程访问，还是让写线程访问；
 *          3 重入性：读取锁和写入锁是否可重入；
 *          4 降级：将写入锁降级为读取锁；
 *          5 升级：将读取锁升级为写入锁；
 *
 *  ---------------_15 原子变量与非阻塞同步机制
 *
 *  168.非阻塞算法中不存在死锁和其他活跃性问题，也不会受到单个线程失败的影响；
 *
 *  169.使用锁存在的问题：
 *      1 线程同时请求锁产生的阻塞挂起到恢复过程中存在着很大的开销，此时线程的调度开销与工作开销的比值会非常高
 *      2 如果锁持有时间非常短，那么在不恰当的时间请求锁时，使线程休眠将付出很高的代价
 *      3 线程等待锁时无法做任何事情，并且持有锁线程出现故障会导致其他线程无法执行
 *      4 当被阻塞线程的优先级较高，持有锁的线程优先级较低，将发生优先级反转；
 *      (当线程在锁上发生竞争时，智能的JVM不一定会挂起线程，而是根据之前获取操作中对锁的操作持有时间长短来判断是
 *      使此线程挂起还是自旋等待)
 *
 *  170.比较并交换(compare and Swap)：包含三个操作
 *          1 需要读写的内存位置V
 *          2 进行比较的值A
 *          3 拟写入的新值B
 *      a 当且仅当内存位置V中的值等于A时，CAS才会通过原子方式用新值B来更新V值，否则不会对V中的值做任何修改；
 *      b 无论V中的值是否等于A，都将返回V原有的值
 *      c 如果有另一个线程在当前线程检查后更新了V中的变量，那么CAS能检测到这个错误 案例 SimulatedCAS.java
 *
 *  171.CAS 典型使用模式是：首先从V中读取值A，使用A计算新值B，然后再通过 CAS 以原子的方式将V中的值由A变成B(只要在这期间没有
 *      任何线程将V的值修改为别的值)。由于CAS能检测到来自其他线程的干扰，因此即使不使用锁也能实现原子的 读-改-写 操作序列；
 *
 *  172.基于 CAS 实现的非阻塞计数器 CasCounter.java
 *      实际上，当竞争程度不高时，基于 CAS 的计数器在性能上远远超过了基于锁的计数器，而在没有竞争时甚至更高
 *
 *  173.从JVM分析 锁与 CAS，虽然锁定语法比较简洁，但是JVM和操作在管理锁时需要完成的工作却并不简单，实现锁定时需要遍历JVM中
 *      一条非常复杂的代码路径，并可能导致操作系统级的锁定、线程挂起以及上下文切换等操作，最好的情况下，锁定时至少需要一次
 *      CAS,因此虽然使用锁时没用到 CAS，但内在实际的开销是相当大的
 *      另一方面，执行CAS不需要执行JVM代码、系统调用或者线程调度操作，都是在应用级以上的代码，
 *
 *   174.CAS的主要缺点是：它将使让调用者自己来处理竞争问题(通过重试、回退、放弃)，而在锁中能自动处理竞争问题(线程获得锁前将
 *      一直阻塞)
 *
 *   175.CAS的执行性能受不同体系架构之间的影响，还可能在不同版本处理器之间发生改变，但通常管用的法则是:在大多数处理器上，在
 *      无竞争的锁获取和释放的"快速代码路径"上的开销，大约是CAS开销的两倍；
 *
 *  176.基于 CAS 的原子变量 如 AtomicInteger 表示一个 int 类型值，这些 volatile 类型的 int 变量在读取和写入上有着相同的内存
 *      语义；
 *
 *  177.原子变量类共有12个，可分为4组：
 *      1 标量类(Scalar)
 *      2 更新器类
 *      3 数组类
 *      4 复合变量类
 *
 *  178.原子数组类(只支持 Integer Long 和 Reference)中的元素可以实现原子更新，volatile 类型数组仅在数组引用上具有 volatile
 *      语义，而在其元素上则没有!!!
 *
 *  179.基本类型的包装类是不可修改的，而原子变量类是可修改的，在原子变量类中没有重新定义 hashCode 或 equals 方法，每个实例
 *      都是不同的，与其他可变对象相同，它们也不宜用作基于散列的容器中的键值！！！
 *
 *  180.使用 CAS 来维持包含多个变量的不变性条件 CasNumberRange.java 把多个变量包装到类里再放入 AtomicReference
 *
 *  181.对比性能：锁与原子变量
 *      基于 ReentrantLock 实现的随机数生成器 ReentrantLockPseudoRandom.java
 *      基于 AtomicInteger 实现的随机数生成器 AtomicPseudoRandom.java
 *      分别在竞争程度较高和适中情况下的两者的性能如图     在竞争程度较高的情况下的Lock与AtomicInteger.png
 *                                                      在竞争程度适中情况下的Lock与AtomicInteger.png
 *      1 图中可看出，高度竞争下，锁的性能将超过原子变量的性能，但在更真实的竞争情况下，原子变量的性能将超过锁的性能，这是
 *        因为锁在竞争时会挂起线程，从而降低了CPU 的使用率和共享内存总线上的同步通信量；
 *
 *      2 这类似于在生产者-消费者设计中的可阻塞生产者，他能降低消费者上的工作负载，使消费者的处理速度赶上生产者的处理速度
 *
 *      3 如果使用原子变量，那么发出调用的类负责对竞争进行管理，与大多数基于CAS的算法一样， AtomicPseudoRandom 在遇到竞争时
 *        将立即重试，这通常是一种正确方法，但再竞争激烈的环境下却导致了更多的竞争；
 *
 *      4 这种结论在其他领域同样成立：当交通拥堵时，交通信号灯能够实现更高的吞吐量，而在低拥堵时，环岛能实现更高的吞吐量，
 *        在以太网中使用的竞争机制在低通信流量的情况下运行的很好，但再高通信流量的情况下，令牌环网络中令牌传递则表现的更好
 *
 *      5 但是我们要意识到，图 15-1 中竞争级别过高而有些不切实际：任何一个真实的程序都不会出了竞争锁或原子变量，其他工作都
 *        不做，在实际情况中，原子变量在可伸缩性上要高于锁，因为在应对常见的竞争程度时，原子变量的效率会更高！！！
 *
 *      总结：在种地程度的竞争下，原子变量能提供更高的可伸缩性，在高度竞争下，锁能够更好的避免竞争
 *
 *  182.非阻塞算法：如果在某种算法中，一个线程的失败或挂起不会导致其他线程也失败或挂起，那么这种算法就被称为非阻塞算法；
 *      无锁算法：如果在算法的每个步骤中都存在某个线程能够执行下去，那么这种算法称为无锁算法 Lock-Free；
 *
 *  183.创建非阻塞算法的关键在于，找出如何将原子修改的范围缩小到单个变量上，同时还要维护数据的一致性；
 *
 *  184.通过原子来构建栈的示例，使用 Treiber 算法构造的非阻塞栈 ConcurrentStack.java 非阻塞计数器 CasCounter.java
 *      两个例子很好的说明了 CAS 的基本使用模式：在更新某个值时存在不确定性，以及在更新失败时将重新尝试
 *      构建非阻塞算法技巧在于：将执行原子修改的范围缩小到单个变量上；
 *
 *  185.非阻塞链表以及实现：
 *      1 链接队列比栈复杂，因为它必须支持对头结点和尾节点的快速访问，因此需要单独维护头指针和尾指针；
 *      2 队列为空时头尾指针指向同一个节点，当要插入新元素时两个指针都要采用原子操作来更新，即需执行要两个CAS；
 *      3 但是两个CAS操作中间可能会插入另一个线程的访问，
 *      4 可能第一个CAS成功第二个CAS失败,这样就会使队列处于不一致的状态；
 *      实现步骤：
 *      1 在包含多个步骤的更新操作中，要确保数据结构总是处于一致的状态：A线程在执行过程中B到达，那么B要等待A执行完成再进行更新
 *        存在的问题：如果A执行更新操作中途失败了，那么其他线程将被阻塞无法再访问队列
 *      2 如果B到达时发现A正在修改数据结构，那么在数据结构中应该有足够的信息，使得能够完成A的更新操作，这样B就无需等待A操作
 *        完成，当A恢复后再视图完成操作时，会发现B已经替他完成了(如果来了第三个线程呢？-进行轮询尝试)
 *      案例：LinkedQueue.java 实现思想
 *      但这里使用 AtomicReference 来构建 Node 会极大的降低插入操作的开销
 *
 *  186.原子的域更新器：它表示现有 volatile 域的一种基于反射的“视图”，从而能够在已有的 volatile 域上使用CAS。
 *      1 域更新器类没有构造函数，需要使用 newUpdater 工厂方法；
 *      2 域更新器类没有与某个特定的实例关联在一起，因此可以更新目标类的任意实例中的域
 *      3 域更新器类提供的原子性比普通原子类更弱一些，因为无法保证底层的域不被直接修改--像compareAndSet以及其他算数方法
 *        只能确保其他使用原子域更新器的线程的原子性；
 *
 *  187.原子的域更新器在 ConcurrentLinkedQueue 中的使用 ConcurrentLinkedQueueUseFiledUpdater
 *      1 虽然实现看起来较为繁琐，但完全是为了提升性能
 *      2 极大多数情况下原子变量的性能都很不错，很少情况下才使用原子的域更新器，如在执行原子更新的同时还需要维持现有的类的串
 *        行化形式，那么原子的域更新器将非常有用；
 *
 *  188.CAS与原子更新域最大的区别：原子更新域只能保证线程原子性，无法保证底层域不被直接修改，而CAS则可以在底层被修改后告诉调用者
 *
 *  189.ABA问题：在CAS操作中判断V中的值为A,但是它可能已经被替换为B后又替换回A，这时候就不能认为V中的对象未发生变化，此时可能
 *      就要重新执行算法中的某些步骤；在计数器中遇到该问题可能会引起更大的问题；
 *      1 何时发生：如果算法中采用自己的方式来管理节点对象的内存，那么就可能出现 ABA问题，
 *      2 解决办法：如果通过垃圾回收期来管理链表节点任然无法避免ABA，那么可以采用在更新节点内容时更新两个值，而不是更新引用
 *                 的值，包括一个引用和版本号；
 *                 AtomicStampedReference将更新一个”对象-引用”二元组，通过在引用上加上“版本号”，避免ABA问题 计数器常用。
 *                 AtomicMarkableReference将更新一个“对象引用-布尔值”二元组。
 *
 *  ----_16 java 内存模型 https://www.jianshu.com/p/bf158fbb2432
 *                        https://blog.csdn.net/huangzhilin2015/article/details/88947930 必看
 *
 *  190.JMM即Java Memory Model（Java内存模型），是一种内存访问规范，定义了程序中共享变量的访问规则（私有变量为线程私有，所以
 *      不会有并发问题，比如局部变量等等）。它提出的最初目的是为了支持多线程程序设计，Java也有必要保证Java程序能够屏蔽各种硬
 *      件和操作系统的内存访问差异，实现在不同的平台下都能支持正确的并发操作，当然在单核情况下依然有效;
 *
 *  190.在Java内存模型中，描述了在多线程代码中，哪些行为是正确的、合法的，以及多线程之间如何进行通信，代码中变量的读写行为如
 *      何反应到内存、CPU缓存的底层细节。
 *
 *  191.Volatile：主要用于线程之间进行通信，volatile字段的每次读行为都能看到其它线程最后一次对该字段的写行为，通过它就可以
 *      避免拿到缓存中陈旧数据。它们必须保证在被写入之后，会被刷新到主内存中，这样就可以立即对其它线程可以见。类似的，在读取
 *      volatile 字段之前，缓存必须是无效的，以保证每次拿到的都是主内存的值，都是最新的值。volatile 的内存语义和 synchronize
 *      获取和释放 monitor 的实现目的是差不多的。
 *
 *      volatile：从最终汇编语言从面来看，volatile使得每次将i进行了修改之后，增加了一个内存屏障lock addl $0x0,(%rsp)保证
 *      修改的值必须刷新到主内存才能进行内存屏障后续的指令操作。但是内存屏障之前的指令并不是原子的。！！
 *
 *      synchronized：则是使用lock cmpxchg %rsi,(%rdi)的原子指令，使得修改是原子操作。如果修改失败，则继续尝试，直到成功。！！
 *
 *  190.哪些因素会使得一个线程无法看到变量的最新值：
 *      1 编译器中生成的指令顺序可以与源代码的顺序不同，此外编译器还会把变量保存在寄存器而不是内存中；
 *      2 处理器可以采用乱序或并行等方式来执行指令；
 *      3 缓存可能会改变将写入变量提交到主内存的次序
 *      4 保存在处理器本地缓存中的值，对于其他处理器是不可见的(多处理器的情况？)
 *
 *  191.JMM规定了JVM必须遵循一组最小保证，这组保证规定了对变量的写入操作在何时将对于其他线程可见；
 *
 *  192.为了使java开发人员无需关心不同架构上内存模型之间的差异，java提供了自己的内存模型，并且jvm通过在适当的位置上插入内存
 *      栅栏来屏蔽在JMM于底层平台的内存模型之间的差异；
 *
 *  193.JVM什么时候会对程序的重排序：JMM为程序中所有的操作定义了一个偏序关系，称之为 Happens-Before,要保证执行操作B的线程看
 *      到A的结果(不管是否在同一线程)，那么A和B之间必须满足 Happens-Before,如果不满足，则JVM将对他们进行任意的重排序；
 *
 *  194.Happens-Before 的程序顺序规则包括：
 *      1 程序顺序规则：如果程序中操作A在操作B之前，那么在线程中A操作将在B操作之前执行；
 *      2 监视器锁规则：在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前；
 *      3 volatile 变量规则：对 volatile 变量的写入操作必须在对该变量的读操作之前执行；
 *      4 线程启动规则：在线程上对 Thread.Start 的调用必须在该线程中执行任何操作之前执行；
 *      5 线程结束规则：线程中的任何操作都必须在其他线程检测到该线程已经结束之前执行，或者从 Thread.join 中成功返回，
 *                     或者在调用 Thread.isAlive 时返回false；
 *      6 中断规则：当一个线程在另个线程上调用 interrupt 时，必须在被中断线程检测到 interrupt 调用之前执行
 *      7 终结器规则：对象的构造函数必须在启动改对象的终接器之前执行完成；
 *      8 传递性：如果操作A在操作B之前执行，并且操作B在操作C之前执行，那么操作A必须在操作C之前执行；
 *
 *  195.一种借助同步技术：将 Happens-Before 排序功能 + 现有同步机制(通常是监视器锁/volatile变量规则)
 *      案例： FutureTask.java 它在设计师能够确保，在调用tryAcquireShared 之前总能成功的调用 tryReleaseShared,
 *            tryReleaseShared 会写入一个 volatile 类型的变量，而 tryAcquireShared 将读取这个变量；
 *
 *      java类库中提供的其他 Happens-Before 包括：
 *          1 BlockingQueue 的实现，一个线程将对象置入队列并且另一个线程随后获取这个对象；
 *          2 将一个元素放入一个线程安全的容器的操作将在另一个线程从该容器中获得这个元素的操作之前执行；
 *          3 在 CountDownLatch 上的倒数操作将在线程从闭锁上的 await 方法中返回之前执行；
 *          4 释放 Semaphore 许可的操作将从该 Semaphore 上获得一个许可之前执行；
 *          5 Future 表示的任务的所有操作将在从 Future.get 中返回之前执行；
 *          6 向 Executor 提交一个 Runnable 或者 Callable 的操作将在任务开始执行之前执行；
 *          7 一个线程到达 CyclicBarrier 或 Exchanger 的操作将在其他到达该栅栏或交换点的线程被释放之前执行，如果
 *            CyclicBarrier 使用一个栅栏操作，那么到达栅栏的操作将在栅栏操作之前执行，而栅栏操作又会在线程从栅栏中释放之前执行；
 *
 *  196.造成不正确发布的真正原因，就是在发布一个共享对象与另一个线程访问该对象之间缺少一种 Happens-Before 排序，因为没有
 *      这种排序规则，A-B线程之间的调用就会发生重排序，在B调用A构造对象之前可能访问到的是不完整的A实例；
 *
 *  197.静态初始化器是由 JVM 在类的初始化阶段执行，即在类被加载之后，被线程使用之前；由于在JVM内部存在着同步机制，
 *      因此通过这种方式初始化的任何对象都可以被安全地发布；
 *          由于 JVM 将在初始化期间获得一个锁，并且每个线程都至少获取一次这个锁以确保这个类已经加载，因此在静态初始化期间，
 *          内存写入操作将自动对所有线程可见。因此无论是在被构造期间还是被引用时，静态初始化的对象都不需要显示的同步。然而这个
 *          规则仅适用于在构造时的状态，如果对象是可变的，那就需要在读写线程之间同步来确保可见性；
 *      简单但不够严谨的说：静态变量是在类初始化阶段初始化的，整个过程是加锁的；
 *
 *  198.对于含有final域的对象，初始化安全性可以防止对对象的初始引用被重排序到构造过程之前；并且在构造函数完成时，构造函数对
 *      final 域的所有写入操作，以及对通过这些域可以到达的任何变量的写入操作都将被冻结 案例  SafeStates.java
 *
 *  199.as-if-serial 语义的意思指：不管怎么重排序（编译器和处理器为了提高并行度），（单线程）程序的执行结果不能被改变。编译
 *      器，runtime 和处理器都必须遵守 as-if-serial 语义。
 *
 *  200.用什么样的思路来表达java内存模型：
 *      1 描述一个场景：CPU 处理器-缓存-主内存 之间的数据操作共享流程,引出 Intel 的 MESI 缓存一致性协议
 *      2 JMM是怎么实现的：内存屏障 memory barriers
 *      3 内存屏障有哪些作用：操作可见性，禁止重排序-带出 Happens-Before 及其规则
 *      4 java 代码中的关键字 volatile、final和synchronized 与内存模型的关系
 *      5 总结来说，JMM是屏蔽掉各种硬件/操作系统的内存访问差异，以实现让java程序在各个平台下都能达到一致的内存访问效果
 *        目标是定义程序中各个变量的访问规则，即在虚拟机中将变量存储到内存和从内存中取出变量这样的底层细节
 *        Java内存模型是围绕着在并发过程中如何处理
 *          原子性(synchronized)、
 *          可见性(synchronized\volatile\final)、
 *          有序性(volatile\synchronized)这三个特征来建立的。 https://www.cnblogs.com/_popc/p/6096517.html
 *
 *      Java内存模型（Java Memory Model ,JMM）就是一种符合内存模型规范的，屏蔽了各种硬件和操作系统的访问差异的，
 *      保证了Java程序在各种平台下对内存的访问都能得到一致效果的机制及规范。目的是解决由于多线程通过共享内存进行通信时，
 *      存在的原子性、可见性（缓存一致性）以及有序性问题。
 *
 *  ************************************************************************************************
 *  201.https://blog.csdn.net/panweiwei1994/article/details/78483167 Java并发编程札记-目录！！！
 *
 * @author: cww
 * @DateTime: 2019/5/28 8:52
 */

public class ConcurrentTheory {
}
